home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Net / Sieve.php < prev    next >
PHP Script  |  2004-03-24  |  36KB  |  1,167 lines

  1. <?php
  2. // +-----------------------------------------------------------------------+
  3. // | Copyright (c) 2002-2003, Richard Heyes                                     |
  4. // | All rights reserved.                                                  |
  5. // |                                                                       |
  6. // | Redistribution and use in source and binary forms, with or without    |
  7. // | modification, are permitted provided that the following conditions    |
  8. // | are met:                                                              |
  9. // |                                                                       |
  10. // | o Redistributions of source code must retain the above copyright      |
  11. // |   notice, this list of conditions and the following disclaimer.       |
  12. // | o Redistributions in binary form must reproduce the above copyright   |
  13. // |   notice, this list of conditions and the following disclaimer in the |
  14. // |   documentation and/or other materials provided with the distribution.|
  15. // | o The names of the authors may not be used to endorse or promote      |
  16. // |   products derived from this software without specific prior written  |
  17. // |   permission.                                                         |
  18. // |                                                                       |
  19. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
  20. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
  21. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22. // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
  23. // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24. // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
  25. // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
  28. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29. // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
  30. // |                                                                       |
  31. // +-----------------------------------------------------------------------+
  32. // | Author: Richard Heyes <richard@phpguru.org>                           |
  33. // | Co-Author: Damian Fernandez Sosa <damlists@cnba.uba.ar>               |
  34. // +-----------------------------------------------------------------------+
  35.  
  36. require_once('Net/Socket.php');
  37.  
  38. /**
  39. * TODO
  40. *
  41. * o supportsAuthMech()
  42. */
  43.  
  44. /**
  45. * Disconnected state
  46. * @const NET_SIEVE_STATE_DISCONNECTED
  47. */
  48. define('NET_SIEVE_STATE_DISCONNECTED',  1, true);
  49.  
  50. /**
  51. * Authorisation state
  52. * @const NET_SIEVE_STATE_AUTHORISATION
  53. */
  54. define('NET_SIEVE_STATE_AUTHORISATION', 2, true);
  55.  
  56. /**
  57. * Transaction state
  58. * @const NET_SIEVE_STATE_TRANSACTION
  59. */
  60. define('NET_SIEVE_STATE_TRANSACTION',   3, true);
  61.  
  62. /**
  63. * A class for talking to the timsieved server which
  64. * comes with Cyrus IMAP. the HAVESPACE
  65. * command which appears to be broken (Cyrus 2.0.16).
  66. *
  67. * @author  Richard Heyes <richard@php.net>
  68. * @author  Damian Fernandez Sosa <damlists@cnba.uba.ar>
  69. * @access  public
  70. * @version 0.9.1
  71. * @package Net_Sieve
  72. */
  73.  
  74. class Net_Sieve
  75. {
  76.     /**
  77.     * The socket object
  78.     * @var object
  79.     */
  80.     var $_sock;
  81.  
  82.     /**
  83.     * Info about the connect
  84.     * @var array
  85.     */
  86.     var $_data;
  87.  
  88.     /**
  89.     * Current state of the connection
  90.     * @var integer
  91.     */
  92.     var $_state;
  93.  
  94.     /**
  95.     * Constructor error is any
  96.     * @var object
  97.     */
  98.     var $_error;
  99.  
  100.  
  101.     /**
  102.     * To allow class debuging
  103.     * @var boolean
  104.     */
  105.     var $_debug = false;
  106.  
  107.  
  108.     /**
  109.     * The auth methods this class support
  110.     * @var array
  111.     */
  112.  
  113.     var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'PLAIN' , 'LOGIN');
  114.     //if you have problems using DIGEST-MD5 authentication  please commente the line above and discomment the following line
  115.     //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
  116.  
  117.     //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
  118.  
  119.  
  120.     /**
  121.     * The auth methods this class support
  122.     * @var array
  123.     */
  124.     var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');
  125.  
  126.  
  127.  
  128.     /**
  129.     * Handles posible referral loops
  130.     * @var array
  131.     */
  132.     var $_maxReferralCount = 15;
  133.  
  134.     /**
  135.     * Constructor
  136.     * Sets up the object, connects to the server and logs in. stores
  137.     * any generated error in $this->_error, which can be retrieved
  138.     * using the getError() method.
  139.     *
  140.     * @access public
  141.     * @param  string $user      Login username
  142.     * @param  string $pass      Login password
  143.     * @param  string $host      Hostname of server
  144.     * @param  string $port      Port of server
  145.     * @param  string $logintype Type of login to perform
  146.     * @param  string $euser     Effective User (if $user=admin, login as $euser)
  147.     */
  148.     function Net_Sieve($user = null , $pass  = null , $host = 'localhost', $port = 2000, $logintype = '', $euser = '', $debug = false)
  149.     {
  150.         $this->_state = NET_SIEVE_STATE_DISCONNECTED;
  151.         $this->_data['user'] = $user;
  152.         $this->_data['pass'] = $pass;
  153.         $this->_data['host'] = $host;
  154.         $this->_data['port'] = $port;
  155.         $this->_data['logintype'] = $logintype;
  156.         $this->_data['euser'] = $euser;
  157.         $this->_sock = &new Net_Socket();
  158.         $this->_debug  = $debug;
  159.         /*
  160.         * Include the Auth_SASL package.  If the package is not available,
  161.         * we disable the authentication methods that depend upon it.
  162.         */
  163.         if ((@include_once 'Auth/SASL.php') === false) {
  164.             if($this->_debug){
  165.                 echo "AUTH_SASL NOT PRESENT!\n";
  166.             }
  167.             foreach($this->supportedSASLAuthMethods as $SASLMethod){
  168.                 $pos = array_search( $SASLMethod, $this->supportedAuthMethods );
  169.                 if($this->_debug){
  170.                     echo "DISABLING METHOD $SASLMethod\n";
  171.                 }
  172.                 unset($this->supportedAuthMethods[$pos]);
  173.             }
  174.         }
  175.         if( ($user != null) && ($pass != null) ){
  176.             $this->_error = $this->_handleConnectAndLogin();
  177.         }
  178.     }
  179.  
  180.  
  181.  
  182.     /**
  183.     * Handles the errors the class can find
  184.     * on the server
  185.     *
  186.     * @access private
  187.     * @return PEAR_Error
  188.     */
  189.  
  190.     function _raiseError($msg, $code)
  191.     {
  192.     include_once 'PEAR.php';
  193.     return PEAR::raiseError($msg, $code);
  194.     }
  195.  
  196.  
  197.  
  198.  
  199.  
  200.     /**
  201.     * Handles connect and login.
  202.     * on the server
  203.     *
  204.     * @access private
  205.     * @return mixed Indexed array of scriptnames or PEAR_Error on failure
  206.     */
  207.     function _handleConnectAndLogin(){
  208.         if (PEAR::isError($res = $this->connect($this->_data['host'] , $this->_data['port'] ))) {
  209.             return $res;
  210.         }
  211.         if (PEAR::isError($res = $this->login($this->_data['user'], $this->_data['pass'], $this->_data['logintype'] , $this->_data['euser'] ) ) ) {
  212.             return $res;
  213.         }
  214.         return true;
  215.  
  216.     }
  217.  
  218.  
  219.  
  220.  
  221.     /**
  222.     * Returns an indexed array of scripts currently
  223.     * on the server
  224.     *
  225.     * @access public
  226.     * @return mixed Indexed array of scriptnames or PEAR_Error on failure
  227.     */
  228.     function listScripts()
  229.     {
  230.         if (is_array($scripts = $this->_cmdListScripts())) {
  231.             $this->_active = $scripts[1];
  232.             return $scripts[0];
  233.         } else {
  234.             return $scripts;
  235.         }
  236.     }
  237.  
  238.     /**
  239.     * Returns the active script
  240.     *
  241.     * @access public
  242.     * @return mixed The active scriptname or PEAR_Error on failure
  243.     */
  244.     function getActive()
  245.     {
  246.         if (!empty($this->_active)) {
  247.             return $this->_active;
  248.  
  249.         } elseif (is_array($scripts = $this->_cmdListScripts())) {
  250.             $this->_active = $scripts[1];
  251.             return $scripts[1];
  252.         }
  253.     }
  254.  
  255.     /**
  256.     * Sets the active script
  257.     *
  258.     * @access public
  259.     * @param  string $scriptname The name of the script to be set as active
  260.     * @return mixed              true on success, PEAR_Error on failure
  261.     */
  262.     function setActive($scriptname)
  263.     {
  264.         return $this->_cmdSetActive($scriptname);
  265.     }
  266.  
  267.     /**
  268.     * Retrieves a script
  269.     *
  270.     * @access public
  271.     * @param  string $scriptname The name of the script to be retrieved
  272.     * @return mixed              The script on success, PEAR_Error on failure
  273.     */
  274.     function getScript($scriptname)
  275.     {
  276.         return $this->_cmdGetScript($scriptname);
  277.     }
  278.  
  279.     /**
  280.     * Adds a script to the server
  281.     *
  282.     * @access public
  283.     * @param  string $scriptname Name of the script
  284.     * @param  string $script     The script
  285.     * @param  bool   $makeactive Whether to make this the active script
  286.     * @return mixed              true on success, PEAR_Error on failure
  287.     */
  288.     function installScript($scriptname, $script, $makeactive = false)
  289.     {
  290.         if (PEAR::isError($res = $this->_cmdPutScript($scriptname, $script))) {
  291.             return $res;
  292.  
  293.         } elseif ($makeactive) {
  294.             return $this->_cmdSetActive($scriptname);
  295.  
  296.         } else {
  297.             return true;
  298.         }
  299.     }
  300.  
  301.     /**
  302.     * Removes a script from the server
  303.     *
  304.     * @access public
  305.     * @param  string $scriptname Name of the script
  306.     * @return mixed              True on success, PEAR_Error on failure
  307.     */
  308.     function removeScript($scriptname)
  309.     {
  310.         return $this->_cmdDeleteScript($scriptname);
  311.     }
  312.  
  313.     /**
  314.     * Returns any error that may have been generated in the
  315.     * constructor
  316.     *
  317.     * @access public
  318.     * @return mixed False if no error, PEAR_Error otherwise
  319.     */
  320.     function getError()
  321.     {
  322.         return PEAR::isError($this->_error) ? $this->_error : false;
  323.     }
  324.  
  325.     /**
  326.     * Handles connecting to the server and checking the
  327.     * response is valid.
  328.     *
  329.     * @access private
  330.     * @param  string $host Hostname of server
  331.     * @param  string $port Port of server
  332.     * @return mixed        True on success, PEAR_Error otherwise
  333.     */
  334.     function connect($host, $port)
  335.     {
  336.         if (NET_SIEVE_STATE_DISCONNECTED != $this->_state) {
  337.             $msg='Not currently in DISCONNECTED state';
  338.             $code=1;
  339.             return $this->_raiseError($msg,$code);
  340.         }
  341.  
  342.         if (PEAR::isError($res = $this->_sock->connect($host, $port, null, 5))) {
  343.             return $res;
  344.         }
  345.  
  346.         if(PEAR::isError($res = $this->_doCmd() )) {
  347.             $msg='Failed to connect, server said: ' . $res->getMessage();
  348.             $code=2;
  349.             return $this->_raiseError($msg,$code);
  350.         }
  351.         // Get logon greeting/capability and parse
  352.         $this->_parseCapability($res);
  353.         $this->_state = NET_SIEVE_STATE_AUTHORISATION;
  354.         return true;
  355.     }
  356.  
  357.     /**
  358.     * Logs into server.
  359.     *
  360.     * @access public
  361.     * @param  string $user      Login username
  362.     * @param  string $pass      Login password
  363.     * @param  string $logintype Type of login method to use
  364.     * @param  string $euser     Effective UID (perform on behalf of $euser)
  365.     * @return mixed             True on success, PEAR_Error otherwise
  366.     */
  367.     function login($user, $pass, $logintype = null , $euser = '')
  368.     {
  369.         if (NET_SIEVE_STATE_AUTHORISATION != $this->_state) {
  370.             $msg='Not currently in AUTHORISATION state';
  371.             $code=1;
  372.             return $this->_raiseError($msg,$code);
  373. //          return PEAR::raiseError('Not currently in AUTHORISATION state');
  374.         }
  375.  
  376.         if(PEAR::isError($res=$this->_cmdAuthenticate($user , $pass , $logintype, $euser ) ) ){
  377.             return $res;
  378.         }
  379. /*
  380.         if (PEAR::isError($res = $this->_doCmd() )) {
  381.             return $res;
  382.         }
  383. */
  384.         $this->_state = NET_SIEVE_STATE_TRANSACTION;
  385.         return true;
  386.     }
  387.  
  388.  
  389.  
  390.      /* Handles the authentication using any known method
  391.      *
  392.      * @param string The userid to authenticate as.
  393.      * @param string The password to authenticate with.
  394.      * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )
  395.      * @param string The effective uid to authenticate as.
  396.      *
  397.      * @return mixed  string or PEAR_Error
  398.      *
  399.      * @access private
  400.      * @since  1.0
  401.      */
  402.     function _cmdAuthenticate($uid , $pwd , $userMethod = null , $euser = '' )
  403.     {
  404.  
  405.  
  406.         if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {
  407.             return $method;
  408.         }
  409.         switch ($method) {
  410.             case 'DIGEST-MD5':
  411.                 $result = $this->_authDigest_MD5( $uid , $pwd , $euser );
  412.                 return $result;
  413.                 break;
  414.             case 'CRAM-MD5':
  415.                 $result = $this->_authCRAM_MD5( $uid , $pwd, $euser);
  416.                 break;
  417.             case 'LOGIN':
  418.                 $result = $this->_authLOGIN( $uid , $pwd , $euser );
  419.                 break;
  420.             case 'PLAIN':
  421.                 $result = $this->_authPLAIN( $uid , $pwd , $euser );
  422.                 break;
  423.             default :
  424.                 $result = new PEAR_Error( "$method is not a supported authentication method" );
  425.                 break;
  426.         }
  427.  
  428.  
  429.         if (PEAR::isError($res = $this->_doCmd() )) {
  430.             return $res;
  431.         }
  432.         return $res;
  433.     }
  434.  
  435.  
  436.  
  437.  
  438.  
  439.  
  440.  
  441.  
  442.  
  443.      /* Authenticates the user using the PLAIN method.
  444.      *
  445.      * @param string The userid to authenticate as.
  446.      * @param string The password to authenticate with.
  447.      * @param string The effective uid to authenticate as.
  448.      *
  449.      * @return array Returns an array containing the response
  450.      *
  451.      * @access private
  452.      * @since  1.0
  453.      */
  454.     function _authPLAIN($user, $pass , $euser )
  455.     {
  456.  
  457.         if ($euser != '') {
  458.             $cmd=sprintf('AUTHENTICATE "PLAIN" "%s"', base64_encode($euser . chr(0) . $user . chr(0) . $pass ) ) ;
  459.         } else {
  460.             $cmd=sprintf('AUTHENTICATE "PLAIN" "%s"', base64_encode( chr(0) . $user . chr(0) . $pass ) );
  461.         }
  462.         return  $this->_sendCmd( $cmd ) ;
  463.  
  464.     }
  465.  
  466.  
  467.  
  468.      /* Authenticates the user using the PLAIN method.
  469.      *
  470.      * @param string The userid to authenticate as.
  471.      * @param string The password to authenticate with.
  472.      * @param string The effective uid to authenticate as.
  473.      *
  474.      * @return array Returns an array containing the response
  475.      *
  476.      * @access private
  477.      * @since  1.0
  478.      */
  479.     function _authLOGIN($user, $pass , $euser )
  480.     {
  481.         $this->_sendCmd('AUTHENTICATE "LOGIN"');
  482.         $this->_doCmd(sprintf('"%s"', base64_encode($user)));
  483.         $this->_doCmd(sprintf('"%s"', base64_encode($pass)));
  484.  
  485.     }
  486.  
  487.  
  488.  
  489.  
  490.      /* Authenticates the user using the CRAM-MD5 method.
  491.      *
  492.      * @param string The userid to authenticate as.
  493.      * @param string The password to authenticate with.
  494.      * @param string The cmdID.
  495.      *
  496.      * @return array Returns an array containing the response
  497.      *
  498.      * @access private
  499.      * @since  1.0
  500.      */
  501.     function _authCRAM_MD5($uid, $pwd, $euser)
  502.     {
  503.     /*
  504.         if ( PEAR::isError($error = $this->_sendCmd( 'AUTHENTICATE "CRAM-MD5"' ) ) ) {
  505.             $this->_error=$error;
  506.             return $error;
  507.         }
  508.     */
  509.  
  510.         if ( PEAR::isError( $challenge = $this->_doCmd( 'AUTHENTICATE "CRAM-MD5"' ) ) ) {
  511.             $this->_error=challenge ;
  512.             return challenge ;
  513.         }
  514.         $challenge = base64_decode( $challenge );
  515.         $cram = &Auth_SASL::factory('crammd5');
  516.         $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );
  517.         if ( PEAR::isError($error = $this->_sendStringResponse( $auth_str ) ) ) {
  518.             $this->_error=$error;
  519.             return $error;
  520.         }
  521.  
  522.     }
  523.  
  524.  
  525.  
  526.      /* Authenticates the user using the DIGEST-MD5 method.
  527.      *
  528.      * @param string The userid to authenticate as.
  529.      * @param string The password to authenticate with.
  530.      * @param string The efective user
  531.      *
  532.      * @return array Returns an array containing the response
  533.      *
  534.      * @access private
  535.      * @since  1.0
  536.      */
  537.     function _authDigest_MD5($uid, $pwd, $euser)
  538.     {
  539.         /*
  540.         if ( PEAR::isError($error = $this->_sendCmd( 'AUTHENTICATE "DIGEST-MD5"' ) ) ) {
  541.             $this->_error=$error;
  542.             return $error;
  543.         }
  544.         */
  545.  
  546.         if ( PEAR::isError( $challenge = $this->_doCmd('AUTHENTICATE "DIGEST-MD5"') ) ) {
  547.             $this->_error=challenge ;
  548.             return challenge ;
  549.         }
  550.         $challenge = base64_decode( $challenge );
  551.         $digest = &Auth_SASL::factory('digestmd5');
  552.         $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "sieve" , $euser));
  553.  
  554.         if ( PEAR::isError($error = $this->_sendStringResponse( $auth_str  ) ) ) {
  555.             $this->_error=$error;
  556.             return $error;
  557.         }
  558.  
  559. ///*
  560.  
  561.         if ( PEAR::isError( $challenge = $this->_doCmd() ) ) {
  562.             $this->_error=$challenge ;
  563.             return $challenge ;
  564.         }
  565.  
  566.     if( strtoupper(substr($challenge,0,2))== 'OK' ){
  567.         return true;
  568.     }
  569.     
  570. //echo "CHALL:$challenge\n";
  571. //*/
  572.     /*
  573.          * We don't use the protocol's third step because SIEVE doesn't allow
  574.          * subsequent authentication, so we just silently ignore it.
  575.          */
  576.  
  577. ///*    
  578.         if ( PEAR::isError($error = $this->_sendStringResponse( '' ) ) ) {
  579.             $this->_error=$error;
  580.             return $error;
  581.         }
  582.  
  583.     if (PEAR::isError($res = $this->_doCmd() )) {
  584.             return $res;
  585.         }
  586.  
  587. //*/    
  588.     }
  589.  
  590.  
  591.  
  592.  
  593.     /**
  594.     * Removes a script from the server
  595.     *
  596.     * @access private
  597.     * @param  string $scriptname Name of the script to delete
  598.     * @return mixed              True on success, PEAR_Error otherwise
  599.     */
  600.     function _cmdDeleteScript($scriptname)
  601.     {
  602.         if (NET_SIEVE_STATE_TRANSACTION != $this->_state) {
  603.             $msg='Not currently in AUTHORISATION state';
  604.             $code=1;
  605.             return $this->_raiseError($msg,$code);
  606.             //return PEAR::raiseError('Not currently in TRANSACTION state');
  607.         }
  608.         if (PEAR::isError($res = $this->_doCmd(sprintf('DELETESCRIPT "%s"', $scriptname) ) )) {
  609.             return $res;
  610.         }
  611.         return true;
  612.     }
  613.  
  614.     /**
  615.     * Retrieves the contents of the named script
  616.     *
  617.     * @access private
  618.     * @param  string $scriptname Name of the script to retrieve
  619.     * @return mixed              The script if successful, PEAR_Error otherwise
  620.     */
  621.     function _cmdGetScript($scriptname)
  622.     {
  623.         if (NET_SIEVE_STATE_TRANSACTION != $this->_state) {
  624.             $msg='Not currently in AUTHORISATION state';
  625.             $code=1;
  626.             return $this->_raiseError($msg,$code);
  627.             //return PEAR::raiseError('Not currently in TRANSACTION state');
  628.         }
  629.  
  630.         if (PEAR::isError($res = $this->_doCmd(sprintf('GETSCRIPT "%s"', $scriptname) ) ) ) {
  631.             return $res;
  632.         }
  633.  
  634.         return preg_replace('/{[0-9]+}\r\n/', '', $res);
  635.     }
  636.  
  637.     /**
  638.     * Sets the ACTIVE script, ie the one that gets run on new mail
  639.     * by the server
  640.     *
  641.     * @access private
  642.     * @param  string $scriptname The name of the script to mark as active
  643.     * @return mixed              True on success, PEAR_Error otherwise
  644.     */
  645.     function _cmdSetActive($scriptname)
  646.     {
  647.         if (NET_SIEVE_STATE_TRANSACTION != $this->_state) {
  648.             $msg='Not currently in AUTHORISATION state';
  649.             $code=1;
  650.             return $this->_raiseError($msg,$code);
  651.             //return PEAR::raiseError('Not currently in TRANSACTION state');
  652.         }
  653.  
  654.         if (PEAR::isError($res = $this->_doCmd(sprintf('SETACTIVE "%s"', $scriptname) ) ) ) {
  655.             return $res;
  656.         }
  657.  
  658.         $this->_activeScript = $scriptname;
  659.         return true;
  660.     }
  661.  
  662.     /**
  663.     * Sends the LISTSCRIPTS command
  664.     *
  665.     * @access private
  666.     * @return mixed Two item array of scripts, and active script on success,
  667.     *               PEAR_Error otherwise.
  668.     */
  669.     function _cmdListScripts()
  670.     {
  671.  
  672.         if (NET_SIEVE_STATE_TRANSACTION != $this->_state) {
  673.             $msg='Not currently in AUTHORISATION state';
  674.             $code=1;
  675.             return $this->_raiseError($msg,$code);
  676.             //return PEAR::raiseError('Not currently in TRANSACTION state');
  677.         }
  678.  
  679.         $scripts = array();
  680.         $activescript = null;
  681.  
  682.         if (PEAR::isError($res = $this->_doCmd('LISTSCRIPTS'))) {
  683.             return $res;
  684.         }
  685.  
  686.         $res = explode("\r\n", $res);
  687.  
  688.         foreach ($res as $value) {
  689.             if (preg_match('/^"(.*)"( ACTIVE)?$/i', $value, $matches)) {
  690.                 $scripts[] = $matches[1];
  691.                 if (!empty($matches[2])) {
  692.                     $activescript = $matches[1];
  693.                 }
  694.             }
  695.         }
  696.  
  697.         return array($scripts, $activescript);
  698.     }
  699.  
  700.     /**
  701.     * Sends the PUTSCRIPT command to add a script to
  702.     * the server.
  703.     *
  704.     * @access private
  705.     * @param  string $scriptname Name of the new script
  706.     * @param  string $scriptdata The new script
  707.     * @return mixed              True on success, PEAR_Error otherwise
  708.     */
  709.     function _cmdPutScript($scriptname, $scriptdata)
  710.     {
  711.         if (NET_SIEVE_STATE_TRANSACTION != $this->_state) {
  712.             $msg='Not currently in TRANSACTION state';
  713.             $code=1;
  714.             return $this->_raiseError($msg,$code);
  715.             //return PEAR::raiseError('Not currently in TRANSACTION state');
  716.         }
  717.  
  718.         if (PEAR::isError($res = $this->_doCmd(sprintf("PUTSCRIPT \"%s\" {%d+}\r\n%s", $scriptname, strlen($scriptdata),$scriptdata ) ))) {
  719.             return $res;
  720.         }
  721.  
  722.         return true;
  723.     }
  724.  
  725.     /**
  726.     * Sends the LOGOUT command and terminates the connection
  727.     *
  728.     * @access private
  729.     * @return mixed True on success, PEAR_Error otherwise
  730.     */
  731.     function _cmdLogout($sendLogoutCMD=true)
  732.     {
  733.         if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) {
  734.             $msg='Not currently connected';
  735.             $code=1;
  736.             return $this->_raiseError($msg,$code);
  737.             //return PEAR::raiseError('Not currently connected');
  738.         }
  739.  
  740.         if($sendLogoutCMD){
  741.             if (PEAR::isError($res = $this->_doCmd('LOGOUT'))) {
  742.                 return $res;
  743.             }
  744.         }
  745.  
  746.         $this->_sock->disconnect();
  747.         $this->_state = NET_SIEVE_STATE_DISCONNECTED;
  748.         return true;
  749.     }
  750.  
  751.     /**
  752.     * Sends the CAPABILITY command
  753.     *
  754.     * @access private
  755.     * @return mixed True on success, PEAR_Error otherwise
  756.     */
  757.     function _cmdCapability()
  758.     {
  759.         if (NET_SIEVE_STATE_TRANSACTION != $this->_state) {
  760.             $msg='Not currently in TRANSACTION state';
  761.             $code=1;
  762.             return $this->_raiseError($msg,$code);
  763.             //return PEAR::raiseError('Not currently in TRANSACTION state');
  764.         }
  765.  
  766.         if (PEAR::isError($res = $this->_doCmd('CAPABILITY'))) {
  767.             return $res;
  768.         }
  769.         $this->_parseCapability($res);
  770.         return true;
  771.     }
  772.  
  773.  
  774.     /**
  775.     * Checks if the server has space to store the script
  776.     * by the server
  777.     *
  778.     * @access public
  779.     * @param  string $scriptname The name of the script to mark as active
  780.     * @return mixed              True on success, PEAR_Error otherwise
  781.     */
  782.     function haveSpace($scriptname,$quota)
  783.     {
  784.         if (NET_SIEVE_STATE_TRANSACTION != $this->_state) {
  785.             $msg='Not currently in TRANSACTION state';
  786.             $code=1;
  787.             return $this->_raiseError($msg,$code);
  788.             //return PEAR::raiseError('Not currently in TRANSACTION state');
  789.         }
  790.  
  791.         if (PEAR::isError($res = $this->_doCmd(sprintf('HAVESPACE "%s" %s', $scriptname, $quota) ) ) ) {
  792.         //if (PEAR::isError($res = $this->_doCmd(sprintf('HAVESPACE %d "%s"',  $quota,$scriptname ) ) ) ) {
  793.             return $res;
  794.         }
  795.  
  796.         return true;
  797.     }
  798.  
  799.  
  800.  
  801.  
  802.     /**
  803.     * Parses the response from the capability command. Storesq
  804.     * the result in $this->_capability
  805.     *
  806.     * @access private
  807.     */
  808.     function _parseCapability($data)
  809.     {
  810.         $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
  811.  
  812.         for ($i = 0; $i < count($data); $i++) {
  813.             if (preg_match('/^"([a-z]+)" ("(.*)")?$/i', $data[$i], $matches)) {
  814.                 switch (strtolower($matches[1])) {
  815.                     case 'implementation':
  816.                         $this->_capability['implementation'] = $matches[3];
  817.                         break;
  818.  
  819.                     case 'sasl':
  820.                         $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
  821.                         break;
  822.  
  823.                     case 'sieve':
  824.                         $this->_capability['extensions'] = preg_split('/\s+/', $matches[3]);
  825.                         break;
  826.  
  827.                     case 'starttls':
  828.                         $this->_capability['starttls'] = true;
  829.                 }
  830.             }
  831.         }
  832.     }
  833.  
  834.     /**
  835.     * Sends a command to the server
  836.     *
  837.     * @access private
  838.     * @param string $cmd The command to send
  839.     */
  840.     function _sendCmd($cmd)
  841.     {
  842.         $status = $this->_sock->getStatus();
  843.         if (PEAR::isError($status) || $status['eof']) {
  844.             return new PEAR_Error( 'Failed to write to socket: (connection lost!) ' );
  845.         }
  846.         if ( PEAR::isError( $error = $this->_sock->write( $cmd . "\r\n" ) ) ) {
  847.             return new PEAR_Error( 'Failed to write to socket: ' . $error->getMessage() );
  848.         }
  849.  
  850.         if( $this->_debug ){
  851.             // C: means this data was sent by  the client (this class)
  852.             echo "C:$cmd\n";
  853.         }
  854.         return true;
  855.  
  856.  
  857.     }
  858.  
  859.  
  860.  
  861.     /**
  862.     * Sends a string response to the server
  863.     *
  864.     * @access private
  865.     * @param string $cmd The command to send
  866.     */
  867.     function _sendStringResponse($str)
  868.     {
  869.         $response='{' .  strlen($str) . "+}\r\n" . $str  ;
  870.         return $this->_sendCmd($response);
  871.     }
  872.  
  873.  
  874.  
  875.  
  876.     function _recvLn()
  877.     {
  878.         $lastline='';
  879.         if (PEAR::isError( $lastline = $this->_sock->gets( 8192 ) ) ) {
  880.             return new PEAR_Error('Failed to write to socket: ' . $lastline->getMessage() );
  881.         }
  882.         $lastline=rtrim($lastline);
  883.         if($this->_debug){
  884.             // S: means this data was sent by  the IMAP Server
  885.             echo "S:$lastline\n" ;
  886.         }
  887.  
  888. /*        if( $lastline === '' ){
  889.             return new PEAR_Error('Failed to receive from the  socket: '  );
  890.         }
  891. */
  892.         return $lastline;
  893.     }
  894.  
  895.  
  896.  
  897.  
  898.  
  899.     /**
  900.     * Send a command and retrieves a response from the server.
  901.     *
  902.     *
  903.     * @access private
  904.     * @param string $cmd The command to send
  905.     * @return mixed Reponse string if an OK response, PEAR_Error if a NO response
  906.     */
  907.     function _doCmd($cmd = '' )
  908.     {
  909.  
  910.         $referralCount=0;
  911.         while($referralCount < $this->_maxReferralCount ){
  912.  
  913.  
  914.             if($cmd != '' ){
  915.                 if(PEAR::isError($error = $this->_sendCmd($cmd) )) {
  916.                     return $error;
  917.                 }
  918.             }
  919.             $response = '';
  920.  
  921.             while (true) {
  922.                     if(PEAR::isError( $line=$this->_recvLn() )){
  923.                         return $line;
  924.                     }
  925.                     if ('ok' === strtolower(substr($line, 0, 2))) {
  926.                         $response .= $line;
  927.                         return rtrim($response);
  928.  
  929.                     } elseif ('no' === strtolower(substr($line, 0, 2))) {
  930.                         // Check for string literal error message
  931.                         if (preg_match('/^no {([0-9]+)\+?}/i', $line, $matches)) {
  932.                             $line .= str_replace("\r\n", ' ', $this->_sock->read($matches[1] + 2 ));
  933.                             if($this->_debug){
  934.                                 echo "S:$line\n";
  935.                             }
  936.                         }
  937.                         $msg=trim($response . substr($line, 2));
  938.                         $code=3;
  939.                         return $this->_raiseError($msg,$code);
  940.                         //return PEAR::raiseError(trim($response . substr($line, 2)));
  941.                     } elseif ('bye' === strtolower(substr($line, 0, 3))) {
  942.  
  943.                         if(PEAR::isError($error = $this->disconnect(false) ) ){
  944.                             $msg="Can't handle bye, The error was= " . $error->getMessage() ;
  945.                             $code=4;
  946.                             return $this->_raiseError($msg,$code);
  947.                             //return PEAR::raiseError("Can't handle bye, The error was= " . $error->getMessage() );
  948.                         }
  949.                         if (preg_match('/^bye \(referral "([^"]+)/i', $line, $matches)) {
  950.                             // Check for referral, then follow it.  Otherwise, carp an error.
  951.                             $this->_data['host'] = $matches[1];
  952.                             if (PEAR::isError($error = $this->_handleConnectAndLogin() ) ){
  953.                                 $msg="Can't follow referral to " . $this->_data['host'] . ", The error was= " . $error->getMessage() ;
  954.                                 $code=5;
  955.                                 return $this->_raiseError($msg,$code);
  956.                                 //return PEAR::raiseError("Can't follow referral to " . $this->_data['host'] . ", The error was= " . $error->getMessage() );
  957.                             }
  958.                             break;
  959.                             // Retry the command
  960.                             if(PEAR::isError($error = $this->_sendCmd($cmd) )) {
  961.                                 return $error;
  962.                             }
  963.                             continue;
  964.                         }
  965.                         $msg=trim($response . $line);
  966.                         $code=6;
  967.                         return $this->_raiseError($msg,$code);
  968.                         //return PEAR::raiseError(trim($response . $line));
  969.                     } elseif (preg_match('/^{([0-9]+)\+?}/i', $line, $matches)) {
  970.                         // Matches String Responses.
  971.                         $line = str_replace("\r\n", ' ', $this->_sock->read($matches[1] + 2 ));
  972.                         if($this->_debug){
  973.                             echo "S:$line\n";
  974.                         }
  975.                         return $line;
  976.                     }
  977.                     $response .= $line . "\r\n";
  978.                     $referralCount++;
  979.                 }
  980.         }
  981.         $msg="Max referral count reached ($referralCount times) Cyrus murder loop error?";
  982.         $code=7;
  983.         return $this->_raiseError($msg,$code);
  984.         //return PEAR::raiseError("Max referral count reached ($referralCount times) Cyrus murder loop error?" );
  985.     }
  986.  
  987.  
  988.  
  989.  
  990.     /**
  991.     * Sets the bebug state
  992.     *
  993.     * @access public
  994.     * @return void
  995.     */
  996.     function setDebug($debug=true)
  997.     {
  998.         $this->_debug=$debug;
  999.     }
  1000.  
  1001.     /**
  1002.     * Disconnect from the Sieve server
  1003.     *
  1004.     * @access public
  1005.     * @param  string $scriptname The name of the script to be set as active
  1006.     * @return mixed              true on success, PEAR_Error on failure
  1007.     */
  1008.     function disconnect($sendLogoutCMD=true)
  1009.     {
  1010.         return $this->_cmdLogout($sendLogoutCMD);
  1011.     }
  1012.  
  1013.  
  1014.     /**
  1015.      * Returns the name of the best authentication method that the server
  1016.      * has advertised.
  1017.      *
  1018.      * @param string if !=null,authenticate with this method ($userMethod).
  1019.      *
  1020.      * @return mixed    Returns a string containing the name of the best
  1021.      *                  supported authentication method or a PEAR_Error object
  1022.      *                  if a failure condition is encountered.
  1023.      * @access private
  1024.      * @since  1.0
  1025.      */
  1026.     function _getBestAuthMethod($userMethod = null)
  1027.     {
  1028.  
  1029.        if( isset($this->_capability['sasl']) ){
  1030.            $serverMethods=$this->_capability['sasl'];
  1031.        }else{
  1032.            // if the server don't send an sasl capability fallback to login auth
  1033.            //return 'LOGIN';
  1034.            return new PEAR_Error("This server don't support any Auth methods SASL problem?");
  1035.        }
  1036.  
  1037.         if($userMethod != null ){
  1038.             $methods = array();
  1039.             $methods[] = $userMethod;
  1040.         }else{
  1041.  
  1042.             $methods = $this->supportedAuthMethods;
  1043.         }
  1044.         if( ($methods != null) && ($serverMethods != null)){
  1045.             foreach ( $methods as $method ) {
  1046.                 if ( in_array( $method , $serverMethods ) ) {
  1047.                     return $method;
  1048.                 }
  1049.             }
  1050.             $serverMethods=implode(',' , $serverMethods );
  1051.             $myMethods=implode(',' ,$this->supportedAuthMethods);
  1052.             return new PEAR_Error("$method NOT supported authentication method!. This server " .
  1053.                 "supports these methods= $serverMethods, but I support $myMethods");
  1054.         }else{
  1055.             return new PEAR_Error("This server don't support any Auth methods");
  1056.         }
  1057.     }
  1058.  
  1059.  
  1060.  
  1061.  
  1062.  
  1063.     /**
  1064.     * Return the list of extensions the server supports
  1065.     *
  1066.     * @access public
  1067.     * @return mixed              array  on success, PEAR_Error on failure
  1068.     */
  1069.     function getExtensions()
  1070.     {
  1071.         if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) {
  1072.             $msg='Not currently connected';
  1073.             $code=7;
  1074.             return $this->_raiseError($msg,$code);
  1075.             //return PEAR::raiseError('Not currently connected');
  1076.         }
  1077.  
  1078.         return $this->_capability['extensions'];
  1079.     }
  1080.  
  1081.  
  1082.  
  1083.  
  1084.  
  1085.     /**
  1086.     * Return true if tyhe server has that extension
  1087.     *
  1088.     * @access public
  1089.     * @param string  the extension to compare
  1090.     * @return mixed              array  on success, PEAR_Error on failure
  1091.     */
  1092.     function hasExtension($extension)
  1093.     {
  1094.         if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) {
  1095.             $msg='Not currently connected';
  1096.             $code=7;
  1097.             return $this->_raiseError($msg,$code);
  1098.             //return PEAR::raiseError('Not currently connected');
  1099.         }
  1100.  
  1101.         if(is_array($this->_capability['extensions'] ) ){
  1102.             foreach( $this->_capability['extensions'] as $ext){
  1103.                 if( trim( strtolower( $ext ) ) === trim( strtolower( $extension ) ) )
  1104.                     return true;
  1105.             }
  1106.         }
  1107.         return false;
  1108.     }
  1109.  
  1110.  
  1111.  
  1112.     /**
  1113.     * Return the list of auth methods the server supports
  1114.     *
  1115.     * @access public
  1116.     * @return mixed              array  on success, PEAR_Error on failure
  1117.     */
  1118.     function getAuthMechs()
  1119.     {
  1120.         if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) {
  1121.             $msg='Not currently connected';
  1122.             $code=7;
  1123.             return $this->_raiseError($msg,$code);
  1124.             //return PEAR::raiseError('Not currently connected');
  1125.         }
  1126.         if(!isset($this->_capability['sasl']) ){
  1127.             $this->_capability['sasl']=array();
  1128.         }
  1129.         return $this->_capability['sasl'];
  1130.     }
  1131.  
  1132.  
  1133.  
  1134.  
  1135.  
  1136.     /**
  1137.     * Return true if tyhe server has that extension
  1138.     *
  1139.     * @access public
  1140.     * @param string  the extension to compare
  1141.     * @return mixed              array  on success, PEAR_Error on failure
  1142.     */
  1143.     function hasAuthMech($method)
  1144.     {
  1145.         if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) {
  1146.             $msg='Not currently connected';
  1147.             $code=7;
  1148.             return $this->_raiseError($msg,$code);
  1149.             //return PEAR::raiseError('Not currently connected');
  1150.         }
  1151.  
  1152.         if(is_array($this->_capability['sasl'] ) ){
  1153.             foreach( $this->_capability['sasl'] as $ext){
  1154.                 if( trim( strtolower( $ext ) ) === trim( strtolower( $method ) ) )
  1155.                     return true;
  1156.             }
  1157.         }
  1158.         return false;
  1159.     }
  1160.  
  1161.  
  1162.  
  1163.  
  1164.  
  1165. }
  1166. ?>
  1167.